noble-curves
Audited & minimal JS implementation of elliptic curve cryptography.
- Short Weierstrass, Edwards, Montgomery curves
- ECDSA, EdDSA, Schnorr, BLS signature schemes, ECDH key agreement
- 🔒 Audited by an independent security firm
- #️⃣ hash to curve
for encoding or hashing an arbitrary string to an elliptic curve point
- 🧜♂️ Poseidon ZK-friendly hash
- 🏎 Ultra-fast, hand-optimized for caveats of JS engines
- 🔍 Unique tests ensure correctness with Wycheproof vectors and
cryptofuzz differential fuzzing
- 🔻 Tree-shaking-friendly: use only what's necessary, other code won't be included
Check out Upgrading if you've previously used single-feature noble
packages (secp256k1,
ed25519).
See Resources for articles and real-world software that uses curves.
This library belongs to noble crypto
noble-crypto — high-security, easily auditable set of contained cryptographic libraries and tools.
- No dependencies, protection against supply chain attacks
- Auditable TypeScript / JS code
- Supported in all major browsers and stable node.js versions
- All releases are signed with PGP keys
- Check out homepage & all libraries:
curves
(4kb versions secp256k1,
ed25519),
hashes
Usage
Browser, deno and node.js are supported:
npm install @noble/curves
For Deno, use it with
npm specifier.
In browser, you could also include the single file from
GitHub's releases page.
The library is tree-shaking-friendly and does not expose root entry point as
import * from '@noble/curves'
. Instead, you need to import specific primitives.
This is done to ensure small size of your apps.
Package consists of two parts:
- Implementations, utilizing one dependency
@noble/hashes
,
providing ready-to-use:
- NIST curves secp256r1/P256, secp384r1/P384, secp521r1/P521
- SECG curve secp256k1
- ed25519/curve25519/x25519/ristretto255, edwards448/curve448/x448
implementing
RFC7748 /
RFC8032 /
ZIP215 standards
- pairing-friendly curves bls12-381, bn254
- Abstract, zero-dependency EC algorithms
Implementations
Each curve can be used in the following way:
import { secp256k1 } from '@noble/curves/secp256k1';
const priv = secp256k1.utils.randomPrivateKey();
const pub = secp256k1.getPublicKey(priv);
const msg = new Uint8Array(32).fill(1);
const sig = secp256k1.sign(msg, priv);
secp256k1.verify(sig, msg, pub) === true;
const privHex = '46c930bc7bb4db7f55da20798697421b98c4175a52c630294d75a84b9c126236';
const pub2 = secp256k1.getPublicKey(privHex);
All curves:
import { secp256k1, schnorr } from '@noble/curves/secp256k1';
import { ed25519, ed25519ph, ed25519ctx, x25519, RistrettoPoint } from '@noble/curves/ed25519';
import { ed448, ed448ph, ed448ctx, x448 } from '@noble/curves/ed448';
import { p256 } from '@noble/curves/p256';
import { p384 } from '@noble/curves/p384';
import { p521 } from '@noble/curves/p521';
import { pallas, vesta } from '@noble/curves/pasta';
import { bls12_381 } from '@noble/curves/bls12-381';
import { bn254 } from '@noble/curves/bn';
import { jubjub } from '@noble/curves/jubjub';
Weierstrass curves feature recovering public keys from signatures and ECDH key agreement:
const sigImprovedSecurity = secp256k1.sign(msg, priv, { extraEntropy: true });
sig.recoverPublicKey(msg) === pub;
const someonesPub = secp256k1.getPublicKey(secp256k1.utils.randomPrivateKey());
const shared = secp256k1.getSharedSecret(priv, someonesPub);
secp256k1 has schnorr signature implementation which follows
BIP340:
import { schnorr } from '@noble/curves/secp256k1';
const priv = schnorr.utils.randomPrivateKey();
const pub = schnorr.getPublicKey(priv);
const msg = new TextEncoder().encode('hello');
const sig = schnorr.sign(msg, priv);
const isValid = schnorr.verify(sig, msg, pub);
ed25519 module has ed25519ctx / ed25519ph variants,
x25519 ECDH and ristretto255.
It follows ZIP215 and can be used in consensus-critical applications:
import { ed25519 } from '@noble/curves/ed25519';
import { ed25519ctx, ed25519ph } from '@noble/curves/ed25519';
import { x25519 } from '@noble/curves/ed25519';
const priv = 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4';
const pub = 'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c';
x25519.getSharedSecret(priv, pub) === x25519.scalarMult(priv, pub);
x25519.getPublicKey(priv) === x25519.scalarMultBase(priv);
import { hashToCurve, encodeToCurve } from '@noble/curves/ed25519';
import { RistrettoPoint } from '@noble/curves/ed25519';
const rp = RistrettoPoint.fromHex(
'6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919'
);
RistrettoPoint.hashToCurve('Ristretto is traditionally a short shot of espresso coffee');
ed448 is similar:
import { ed448, ed448ph, ed448ctx, x448 } from '@noble/curves/ed448';
import { hashToCurve, encodeToCurve } from '@noble/curves/ed448';
ed448.getPublicKey(ed448.utils.randomPrivateKey());
Every curve has params:
import { secp256k1 } from '@noble/curves/secp256k1';
console.log(secp256k1.CURVE.p, secp256k1.CURVE.n, secp256k1.CURVE.a, secp256k1.CURVE.b);
BLS12-381 pairing-friendly Barreto-Lynn-Scott elliptic curve construction allows to
construct zk-SNARKs at the 128-bit security
and use aggregated, batch-verifiable
threshold signatures,
using Boneh-Lynn-Shacham signature scheme. Compatible with ETH and others,
just make sure to provide correct DST (domain separation tag argument).
import { bls12_381 as bls } from '@noble/curves/bls12-381';
const privateKey = '67d53f170b908cabb9eb326c3c337762d59289a8fec79f7bc9254b584b73265c';
const message = '64726e3da8';
const publicKey = bls.getPublicKey(privateKey);
const signature = bls.sign(message, privateKey);
const isValid = bls.verify(signature, message, publicKey);
console.log({ publicKey, signature, isValid });
const privateKeys = [
'18f020b98eb798752a50ed0563b079c125b0db5dd0b1060d1c1b47d4a193e1e4',
'ed69a8c50cf8c9836be3b67c7eeff416612d45ba39a5c099d48fa668bf558c9c',
'16ae669f3be7a2121e17d0c68c05a8f3d6bef21ec0f2315f1d7aec12484e4cf5',
];
const messages = ['d2', '0d98', '05caf3'];
const publicKeys = privateKeys.map(bls.getPublicKey);
const signatures2 = privateKeys.map((p) => bls.sign(message, p));
const aggPubKey2 = bls.aggregatePublicKeys(publicKeys);
const aggSignature2 = bls.aggregateSignatures(signatures2);
const isValid2 = bls.verify(aggSignature2, message, aggPubKey2);
console.log({ signatures2, aggSignature2, isValid2 });
const signatures3 = privateKeys.map((p, i) => bls.sign(messages[i], p));
const aggSignature3 = bls.aggregateSignatures(signatures3);
const isValid3 = bls.verifyBatch(aggSignature3, messages, publicKeys);
console.log({ publicKeys, signatures3, aggSignature3, isValid3 });
Abstract API
Abstract API allows to define custom curves. All arithmetics is done with JS
bigints over finite fields, which is defined from modular
sub-module. For
scalar multiplication, we use
precomputed tables with w-ary non-adjacent form (wNAF).
Precomputes are enabled for weierstrass and edwards BASE points of a curve. You
could precompute any other point (e.g. for ECDH) using utils.precompute()
method: check out examples.
There are following zero-dependency algorithms:
abstract/weierstrass: Short Weierstrass curve
import { weierstrass } from '@noble/curves/abstract/weierstrass';
import { Field } from '@noble/curves/abstract/modular';
import { sha256 } from '@noble/hashes/sha256';
import { hmac } from '@noble/hashes/hmac';
import { concatBytes, randomBytes } from '@noble/hashes/utils';
const secq256k1 = weierstrass({
a: 0n,
b: 7n,
Fp: Field(2n ** 256n - 432420386565659656852420866394968145599n),
n: 2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n,
Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n,
Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n,
hash: sha256,
hmac: (key: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)),
randomBytes,
});
Short Weierstrass curve's formula is y² = x³ + ax + b
. weierstrass
expects arguments a
, b
, field Fp
, curve order n
, cofactor h
and coordinates Gx
, Gy
of generator point.
k
generation is done deterministically, following
RFC6979. For this you will need
hmac
& hash
, which in our implementations is provided by noble-hashes. If
you're using different hashing library, make sure to wrap it in the following interface:
type CHash = {
(message: Uint8Array): Uint8Array;
blockLen: number;
outputLen: number;
create(): any;
};
Weierstrass points:
- Exported as
ProjectivePoint
- Represented in projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
- Use complete exception-free formulas for addition and doubling
- Can be decoded/encoded from/to Uint8Array / hex strings using
ProjectivePoint.fromHex
and ProjectivePoint#toRawBytes()
- Have
assertValidity()
which checks for being on-curve - Have
toAffine()
and x
/ y
getters which convert to 2d xy affine coordinates
type SignOpts = { lowS?: boolean; prehash?: boolean; extraEntropy: boolean | Uint8Array };
type CurveFn = {
CURVE: ReturnType<typeof validateOpts>;
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType;
verify: (
signature: Hex | SignatureType,
msgHash: Hex,
publicKey: Hex,
opts?: { lowS?: boolean; prehash?: boolean }
) => boolean;
ProjectivePoint: ProjectivePointConstructor;
Signature: SignatureConstructor;
utils: {
normPrivateKeyToScalar: (key: PrivKey) => bigint;
isValidPrivateKey(key: PrivKey): boolean;
randomPrivateKey: () => Uint8Array;
precompute: (windowSize?: number, point?: ProjPointType<bigint>) => ProjPointType<bigint>;
};
};
interface ProjPointType<T> extends Group<ProjPointType<T>> {
readonly px: T;
readonly py: T;
readonly pz: T;
multiply(scalar: bigint): ProjPointType<T>;
multiplyUnsafe(scalar: bigint): ProjPointType<T>;
multiplyAndAddUnsafe(Q: ProjPointType<T>, a: bigint, b: bigint): ProjPointType<T> | undefined;
toAffine(iz?: T): AffinePoint<T>;
isTorsionFree(): boolean;
clearCofactor(): ProjPointType<T>;
assertValidity(): void;
hasEvenY(): boolean;
toRawBytes(isCompressed?: boolean): Uint8Array;
toHex(isCompressed?: boolean): string;
}
interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
new (x: T, y: T, z: T): ProjPointType<T>;
fromAffine(p: AffinePoint<T>): ProjPointType<T>;
fromHex(hex: Hex): ProjPointType<T>;
fromPrivateKey(privateKey: PrivKey): ProjPointType<T>;
}
ECDSA signatures are represented by Signature
instances and can be
described by the interface:
interface SignatureType {
readonly r: bigint;
readonly s: bigint;
readonly recovery?: number;
assertValidity(): void;
addRecoveryBit(recovery: number): SignatureType;
hasHighS(): boolean;
normalizeS(): SignatureType;
recoverPublicKey(msgHash: Hex): ProjPointType<bigint>;
toCompactRawBytes(): Uint8Array;
toCompactHex(): string;
toDERRawBytes(): Uint8Array;
toDERHex(): string;
}
type SignatureConstructor = {
new (r: bigint, s: bigint): SignatureType;
fromCompact(hex: Hex): SignatureType;
fromDER(hex: Hex): SignatureType;
};
More examples:
const priv = secq256k1.utils.randomPrivateKey();
secq256k1.getPublicKey(priv);
const sig = secq256k1.sign(msg, priv);
secq256k1.verify(sig, msg, priv);
const Point = secq256k1.ProjectivePoint;
const point = Point.BASE;
point.add(point).equals(point.double());
point.subtract(point).equals(Point.ZERO);
point.negate();
point.multiply(31415n);
point.assertValidity();
point.toAffine();
secq256k1.CURVE.n;
secq256k1.CURVE.p;
secq256k1.CURVE.Fp.mod();
secq256k1.CURVE.hash();
const fast = secq256k1.utils.precompute(8, Point.fromHex(someonesPubKey));
fast.multiply(privKey);
abstract/edwards: Twisted Edwards curve
import { twistedEdwards } from '@noble/curves/abstract/edwards';
import { Field } from '@noble/curves/abstract/modular';
import { sha512 } from '@noble/hashes/sha512';
import { randomBytes } from '@noble/hashes/utils';
const Fp = Field(2n ** 255n - 19n);
const ed25519 = twistedEdwards({
a: -1n,
d: Fp.div(-121665n, 121666n),
Fp: Fp,
n: 2n ** 252n + 27742317777372353535851937790883648493n,
h: 8n,
Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n,
Gy: 46316835694926478169428394003475163141307993866256225615783033603165251855960n,
hash: sha512,
randomBytes,
adjustScalarBytes(bytes) {
bytes[0] &= 248;
bytes[31] &= 127;
bytes[31] |= 64;
return bytes;
},
} as const);
Twisted Edwards curve's formula is ax² + y² = 1 + dx²y²
. You must specify a
, d
, field Fp
, order n
, cofactor h
and coordinates Gx
, Gy
of generator point.
For EdDSA signatures, hash
param required. adjustScalarBytes
which instructs how to change private scalars could be specified.
Edwards points:
- Exported as
ExtendedPoint
- Represented in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z)
- Use complete exception-free formulas for addition and doubling
- Can be decoded/encoded from/to Uint8Array / hex strings using
ExtendedPoint.fromHex
and ExtendedPoint#toRawBytes()
- Have
assertValidity()
which checks for being on-curve - Have
toAffine()
and x
/ y
getters which convert to 2d xy affine coordinates - Have
isTorsionFree()
, clearCofactor()
and isSmallOrder()
utilities to handle torsions
type CurveFn = {
CURVE: ReturnType<typeof validateOpts>;
getPublicKey: (privateKey: Hex) => Uint8Array;
sign: (message: Hex, privateKey: Hex, context?: Hex) => Uint8Array;
verify: (sig: SigType, message: Hex, publicKey: Hex, context?: Hex) => boolean;
ExtendedPoint: ExtPointConstructor;
utils: {
randomPrivateKey: () => Uint8Array;
getExtendedPublicKey: (key: PrivKey) => {
head: Uint8Array;
prefix: Uint8Array;
scalar: bigint;
point: PointType;
pointBytes: Uint8Array;
};
};
};
interface ExtPointType extends Group<ExtPointType> {
readonly ex: bigint;
readonly ey: bigint;
readonly ez: bigint;
readonly et: bigint;
assertValidity(): void;
multiply(scalar: bigint): ExtPointType;
multiplyUnsafe(scalar: bigint): ExtPointType;
isSmallOrder(): boolean;
isTorsionFree(): boolean;
clearCofactor(): ExtPointType;
toAffine(iz?: bigint): AffinePoint<bigint>;
}
interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtPointType;
fromAffine(p: AffinePoint<bigint>): ExtPointType;
fromHex(hex: Hex): ExtPointType;
fromPrivateKey(privateKey: Hex): ExtPointType;
}
abstract/montgomery: Montgomery curve
import { montgomery } from '@noble/curves/abstract/montgomery';
import { Field } from '@noble/curves/abstract/modular';
const x25519 = montgomery({
a: 486662n,
Gu: 9n,
Fp: Field(2n ** 255n - 19n),
montgomeryBits: 255,
nByteLength: 32,
adjustScalarBytes(bytes) {
bytes[0] &= 248;
bytes[31] &= 127;
bytes[31] |= 64;
return bytes;
},
});
The module contains methods for x-only ECDH on Curve25519 / Curve448 from RFC7748.
Proper Elliptic Curve Points are not implemented yet.
You must specify curve params Fp
, a
, Gu
coordinate of u, montgomeryBits
and nByteLength
.
abstract/hash-to-curve: Hashing strings to curve points
The module allows to hash arbitrary strings to elliptic curve points. Implements hash-to-curve v16.
Every curve has exported hashToCurve
and encodeToCurve
methods. You should always prefer hashToCurve
for security:
import { hashToCurve, encodeToCurve } from '@noble/curves/secp256k1';
import { randomBytes } from '@noble/hashes/utils';
hashToCurve('0102abcd');
console.log(hashToCurve(randomBytes()));
console.log(encodeToCurve(randomBytes()));
import { bls12_381 } from '@noble/curves/bls12-381';
bls12_381.G1.hashToCurve(randomBytes(), { DST: 'another' });
bls12_381.G2.hashToCurve(randomBytes(), { DST: 'custom' });
If you need low-level methods from spec:
expand_message_xmd
(spec) produces a uniformly random byte string using a cryptographic hash function H that outputs b bits.
Hash must conform to CHash
interface (see weierstrass section).
function expand_message_xmd(
msg: Uint8Array,
DST: Uint8Array,
lenInBytes: number,
H: CHash
): Uint8Array;
function expand_message_xof(
msg: Uint8Array,
DST: Uint8Array,
lenInBytes: number,
k: number,
H: CHash
): Uint8Array;
hash_to_field(msg, count, options)
(spec)
hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
type UnicodeOrBytes = string | Uint8Array;
type Opts = {
DST: UnicodeOrBytes;
p: bigint;
m: number;
k: number;
expand?: 'xmd' | 'xof';
hash: CHash;
};
function hash_to_field(msg: Uint8Array, count: number, options: Opts): bigint[][];
abstract/poseidon: Poseidon hash
Implements Poseidon ZK-friendly hash.
There are many poseidon variants with different constants.
We don't provide them: you should construct them manually.
Check out micro-starknet package for a proper example.
import { poseidon } from '@noble/curves/abstract/poseidon';
type PoseidonOpts = {
Fp: Field<bigint>;
t: number;
roundsFull: number;
roundsPartial: number;
sboxPower?: number;
reversePartialPowIdx?: boolean;
mds: bigint[][];
roundConstants: bigint[][];
};
const instance = poseidon(opts: PoseidonOpts);
abstract/bls
The module abstracts BLS (Barreto-Lynn-Scott) primitives. In theory you should be able to write BLS12-377, BLS24,
and others with it.
abstract/modular: Modular arithmetics utilities
import * as mod from '@noble/curves/abstract/modular';
const fp = mod.Field(2n ** 255n - 19n);
fp.mul(591n, 932n);
fp.pow(481n, 11024858120n);
fp.div(5n, 17n);
fp.sqrt(21n);
mod.mod(21n, 10n);
mod.invert(17n, 10n);
mod.invertBatch([1n, 2n, 4n], 21n);
Creating private keys from hashes
Suppose you have sha256(something)
(e.g. from HMAC) and you want to make a private key from it.
Even though p256 or secp256k1 may have 32-byte private keys,
and sha256 output is also 32-byte, you can't just use it and reduce it modulo CURVE.n
.
Doing so will make the result key biased.
To avoid the bias, we implement FIPS 186 B.4.1, which allows to take arbitrary
byte array and produce valid scalars / private keys with bias being neglible.
Use hash-to-curve if you need
hashing to public keys; the function in the module instead operates on private keys.
import { p256 } from '@noble/curves/p256';
import { sha256 } from '@noble/hashes/sha256';
import { hkdf } from '@noble/hashes/hkdf';
const someKey = new Uint8Array(32).fill(2);
const derived = hkdf(sha256, someKey, undefined, 'application', 40);
const validPrivateKey = mod.hashToPrivateScalar(derived, p256.CURVE.n);
abstract/utils: General utilities
import * as utils from '@noble/curves/abstract/utils';
utils.bytesToHex(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
utils.hexToBytes('deadbeef');
utils.hexToNumber();
utils.bytesToNumberBE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
utils.bytesToNumberLE(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
utils.numberToBytesBE(123n, 32);
utils.numberToBytesLE(123n, 64);
utils.numberToHexUnpadded(123n);
utils.concatBytes(Uint8Array.from([0xde, 0xad]), Uint8Array.from([0xbe, 0xef]));
utils.nLength(255n);
utils.equalBytes(Uint8Array.from([0xde]), Uint8Array.from([0xde]));
Security
- The library has been audited during Jan-Feb 2023 by an independent security firm Trail of Bits:
PDF.
The audit has been funded by Ryan Shea. Audit scope was abstract modules
curve
, hash-to-curve
, modular
, poseidon
, utils
, weierstrass
, and top-level modules _shortw_utils
and secp256k1
. See changes since audit. - The library has been fuzzed by Guido Vranken's cryptofuzz. You can run the fuzzer by yourself to check it.
- Timing attack considerations: JIT-compiler and Garbage Collector make "constant time" extremely hard to achieve in a scripting language. Which means any other JS library can't have constant-timeness. Even statically typed Rust, a language without GC, makes it harder to achieve constant-time for some cases. If your goal is absolute security, don't use any JS lib — including bindings to native ones. Use low-level libraries & languages. Nonetheless we're targetting algorithmic constant time.
We consider infrastructure attacks like rogue NPM modules very important; that's why it's crucial to minimize the amount of 3rd-party dependencies & native bindings. If your app uses 500 dependencies, any dep could get hacked and you'll be downloading malware with every npm install
. Our goal is to minimize this attack vector. As for devDependencies used by the library:
@scure
base, bip32, bip39 (used in tests), micro-bmark (benchmark), micro-should (testing) are developed by us
and follow the same practices such as: minimal library size, auditability, signed releases- prettier (linter), fast-check (property-based testing),
typescript versions are locked and rarely updated. Every update is checked with
npm-diff
.
The packages are big, which makes it hard to audit their source code thoroughly and fully. - They are only used if you clone the git repo and want to add some feature to it. End-users won't use them.
Speed
Benchmark results on Apple M2 with node v19:
secp256k1
init x 58 ops/sec @ 17ms/op
getPublicKey x 5,640 ops/sec @ 177μs/op
sign x 3,909 ops/sec @ 255μs/op
verify x 780 ops/sec @ 1ms/op
getSharedSecret x 465 ops/sec @ 2ms/op
recoverPublicKey x 740 ops/sec @ 1ms/op
schnorr.sign x 597 ops/sec @ 1ms/op
schnorr.verify x 775 ops/sec @ 1ms/op
P256
init x 31 ops/sec @ 31ms/op
getPublicKey x 5,607 ops/sec @ 178μs/op
sign x 3,930 ops/sec @ 254μs/op
verify x 540 ops/sec @ 1ms/op
P384
init x 15 ops/sec @ 63ms/op
getPublicKey x 2,622 ops/sec @ 381μs/op
sign x 1,913 ops/sec @ 522μs/op
verify x 222 ops/sec @ 4ms/op
P521
init x 8 ops/sec @ 119ms/op
getPublicKey x 1,371 ops/sec @ 729μs/op
sign x 1,090 ops/sec @ 917μs/op
verify x 118 ops/sec @ 8ms/op
ed25519
init x 47 ops/sec @ 20ms/op
getPublicKey x 9,414 ops/sec @ 106μs/op
sign x 4,516 ops/sec @ 221μs/op
verify x 912 ops/sec @ 1ms/op
ed448
init x 17 ops/sec @ 56ms/op
getPublicKey x 3,363 ops/sec @ 297μs/op
sign x 1,615 ops/sec @ 619μs/op
verify x 319 ops/sec @ 3ms/op
ecdh
├─x25519 x 1,337 ops/sec @ 747μs/op
├─secp256k1 x 461 ops/sec @ 2ms/op
├─P256 x 441 ops/sec @ 2ms/op
├─P384 x 179 ops/sec @ 5ms/op
├─P521 x 93 ops/sec @ 10ms/op
└─x448 x 496 ops/sec @ 2ms/op
bls12-381
init x 32 ops/sec @ 30ms/op
getPublicKey 1-bit x 858 ops/sec @ 1ms/op
getPublicKey x 858 ops/sec @ 1ms/op
sign x 49 ops/sec @ 20ms/op
verify x 34 ops/sec @ 28ms/op
pairing x 94 ops/sec @ 10ms/op
aggregatePublicKeys/8 x 116 ops/sec @ 8ms/op
aggregatePublicKeys/32 x 31 ops/sec @ 31ms/op
aggregatePublicKeys/128 x 7 ops/sec @ 125ms/op
aggregateSignatures/8 x 45 ops/sec @ 22ms/op
aggregateSignatures/32 x 11 ops/sec @ 84ms/op
aggregateSignatures/128 x 3 ops/sec @ 332ms/opp
hash-to-curve
hash_to_field x 850,340 ops/sec @ 1μs/op
hashToCurve
├─secp256k1 x 1,850 ops/sec @ 540μs/op
├─P256 x 3,352 ops/sec @ 298μs/op
├─P384 x 1,367 ops/sec @ 731μs/op
├─P521 x 691 ops/sec @ 1ms/op
├─ed25519 x 2,492 ops/sec @ 401μs/op
└─ed448 x 1,045 ops/sec @ 956μs/op
Contributing & testing
- Clone the repository
npm install
to install build dependencies like TypeScriptnpm run build
to compile TypeScript codenpm run test
will execute all main tests
Resources
Article about some of library's features: Learning fast elliptic-curve cryptography
Projects using the library:
- secp256k1
- ed25519
- BLS12-381
- Others
Upgrading
Previously, the library was split into single-feature packages
noble-secp256k1 and noble-ed25519. curves can be thought as a continuation of their
original work. The libraries now changed their direction towards providing
minimal 4kb implementations of cryptography and are not as feature-complete.
Upgrading from @noble/secp256k1 1.7:
getPublicKey
- now produce 33-byte compressed signatures by default
- to use old behavior, which produced 65-byte uncompressed keys, set
argument
isCompressed
to false
: getPublicKey(priv, false)
sign
- is now sync; use
signAsync
for async version - now returns
Signature
instance with { r, s, recovery }
properties canonical
option was renamed to lowS
recovered
option has been removed because recovery bit is always returned nowder
option has been removed. There are 2 options:
- Use compact encoding:
fromCompact
, toCompactRawBytes
, toCompactHex
.
Compact encoding is simply a concatenation of 32-byte r and 32-byte s. - If you must use DER encoding, switch to noble-curves (see above).
verify
strict
option was renamed to lowS
getSharedSecret
- now produce 33-byte compressed signatures by default
- to use old behavior, which produced 65-byte uncompressed keys, set
argument
isCompressed
to false
: getSharedSecret(a, b, false)
recoverPublicKey(msg, sig, rec)
was changed to sig.recoverPublicKey(msg)
number
type for private keys have been removed: use bigint
insteadPoint
(2d xy) has been changed to ProjectivePoint
(3d xyz)utils
were split into utils
(same api as in noble-curves) and
etc
(hmacSha256Sync
and others)
Upgrading from @noble/ed25519 1.7:
- Methods are now sync by default
bigint
is no longer allowed in getPublicKey
, sign
, verify
. Reason: ed25519 is LE, can lead to bugsPoint
(2d xy) has been changed to ExtendedPoint
(xyzt)Signature
was removed: just use raw bytes or hex nowutils
were split into utils
(same api as in noble-curves) and
etc
(sha512Sync
and others)getSharedSecret
was moved to x25519
module
License
The MIT License (MIT)
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
See LICENSE file.